package kcp

import (
	"net"
	"strings"

	"gddgame.cc/galaxy/satellite"

	kcpGo "gddgame.cc/galaxy/satellite/transport/kcp/kcp"
	"gddgame.cc/galaxy/utils/def"
)

type kcp struct {
	def.Logger

	addr      *net.UDPAddr
	listener  *kcpGo.Listener
	connected bool
	accept    chan satellite.Conn
}

func NewTransport(udpAddr *net.UDPAddr) *kcp {
	k := &kcp{
		addr: udpAddr,
	}
	return k
}

func (transport *kcp) Dial() (satellite.Conn, error) {
	conn, err := kcpGo.Dial(transport.addr.String())
	if err != nil {
		return nil, err
	}
	return satellite.NewBaseConn(conn, transport), err
}

func (transport *kcp) Listen() (<-chan satellite.Conn, error) {
	listener, err := kcpGo.ListenWithOptions(transport.addr.String(), nil, 0, 0)
	if err != nil {
		return nil, err
	}
	transport.listener = listener
	transport.connected = true
	if transport.Logger != nil {
		transport.Debugf("Listen kcp: %#v", transport.addr)
	}
	transport.accept = make(chan satellite.Conn)
	go func() {
		for {
			if !transport.connected {
				goto Close
			}
			conn, err := transport.listener.AcceptKCP()
			if err != nil {
				if strings.Contains(err.Error(), "broken") {
					goto Close
				}
				if transport.Logger != nil {
					transport.Error(err)
				}
			} else {
				if transport.Logger != nil {
					transport.Debugf("kcp new conn:%#v", conn.LocalAddr(), conn.RemoteAddr())
				}
				//https://github.com/skywind3000/kcp/blob/master/README.en.md#protocol-configuration
				conn.SetStreamMode(true)
				conn.SetWriteDelay(false)
				conn.SetACKNoDelay(true)

				conn.SetNoDelay(1, 10, 2, 1)
				//conn.SetMtu(1024)
				conn.SetWindowSize(128, 128)

				transport.accept <- satellite.NewBaseConn(conn, transport)
			}
		}
	Close:
		//fmt.Println("kcp accept close")
		close(transport.accept)
		_ = transport.Close()
	}()
	return transport.accept, nil
}

func (transport *kcp) Type() satellite.TransportType {
	return "udp"
}
func (transport *kcp) Schema() string {
	return transport.addr.String()
}
func (transport *kcp) SetLogger(logger def.Logger) {
	transport.Logger = logger
}

func (transport *kcp) Close() error {
	if !transport.connected {
		return nil
	}
	//fmt.Println("close tcp")
	transport.connected = false
	err := transport.listener.Close()
	if err != nil {
		return err
	}
	return nil
}

func (transport *kcp) Connected() bool {
	return transport.connected
}
